package org.wutopia.labcloud.domain.uaa.service.impl;


import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.nimbusds.jose.JOSEException;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;


import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.data.redis.core.RedisTemplate;

import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;
import org.wutopia.labcloud.domain.uaa.constant.AccountType;
import org.wutopia.labcloud.domain.uaa.constant.BucketTpye;
import org.wutopia.labcloud.domain.uaa.constant.Role;
import org.wutopia.labcloud.domain.uaa.controller.vo.input.AccountLoginInputVO;
import org.wutopia.labcloud.domain.uaa.controller.vo.input.AccountRegisterInputVO;
import org.wutopia.labcloud.domain.uaa.controller.vo.input.UserAccountEmailCodeInputVO;
import org.wutopia.labcloud.domain.uaa.controller.vo.input.UserAccountLoginByEmailInputVO;
import org.wutopia.labcloud.domain.uaa.controller.vo.output.AccountOutputVO;
import org.wutopia.labcloud.domain.uaa.dao.*;
import org.wutopia.labcloud.domain.uaa.dao.entity.*;
import org.wutopia.labcloud.domain.uaa.mq.producer.UaaProducer;
import org.wutopia.labcloud.domain.uaa.service.UserAccountService;
import org.wutopia.labcloud.library.common.utility.JwtUtility;
import org.wutopia.labcloud.service.api.service.dubbo.FileService;

import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Service
@AllArgsConstructor
@Slf4j
public class UserAccountServiceImpl implements UserAccountService {

    private UserMainDao userMainDao;

    private UserAccountDao userAccountDao;

    private UserAuthRoleDao userAuthRoleDao;

    private UserAuthUserRoleRDao userAuthUserRoleRDao;

    private UserAuthRoleAuthorityRDao userAuthRoleAuthorityRDao;

    private UserAuthAuthorityDao userAuthAuthorityDao;

    private UserAuthUserAuthorityRDao userAuthUserAuthorityRDao;

    private RedisTemplate<String, Object> redisTemplate;

    private PasswordEncoder passwordEncoder;

    private UaaProducer uaaProducer;

    private HttpClient client;

    private RestTemplate restTemplate;

    @DubboReference
    private FileService fileService;

    /**
     * 账号密码 注册
     * @param vo 账号、密码、名称、头像地址
     * @return outputVO
     */
    @Override
    @Transactional
    public AccountOutputVO registerAccount(AccountRegisterInputVO vo) throws JOSEException {


        // 1. 插入数据库
        // 1.1 插入user_main表数据 - username 和 avatarUrl(头像文件需要用OSS文件上传后获取桶和图片名)
        UserMain userMain = new UserMain();
        userMain.setNickname(vo.getNickname());
        userMain.setAvatarAddr(vo.getAvatarAddr());
        userMain.setUsername(vo.getUsername());
        userMain.setPassword(passwordEncoder.encode(vo.getPassword()));

        userMainDao.insert(userMain);
        log.debug(userMain.getId().toString());
        // 1.2 插入user_account表数据
        if(vo.getEmail() != null) {
            UserAccount userAccount = new UserAccount();
            userAccount.setUserId(userMain.getId());
            userAccount.setAuthKey(vo.getEmail());
            userAccount.setType(AccountType.EMAIL);
            userAccountDao.insert(userAccount);
        }


        // 1.3 关联用户到基础用户权限
        Set<String> userAuthorities = bindBaseUserAuth(userMain.getId());

        // 2. 生成token

        // 2.1 使用 user的 id  username userAuthAuthority_List生成 accessToken - 使用 nimbus - jwt 库
        String accessToken = JwtUtility.generateAccessToken(userMain.getId(), userMain.getUsername(), userAuthorities);

        // 2.2 使用 user id 生成 refreshToken - 同上
        String refreshToken = JwtUtility.generateRefreshToken(userMain.getId());

        // 3. 组装outputVO
        AccountOutputVO accountOutputVO = new AccountOutputVO();
        // 3.1 组装 id username avatarUrl
        accountOutputVO.setId(userMain.getId());
        accountOutputVO.setNickname(userMain.getNickname());
        accountOutputVO.setUsername(userMain.getUsername());

        String[] split = userMain.getAvatarAddr().split(":");
        String fileUrl = fileService.getFileUrl(split[0], split[1]);

        accountOutputVO.setAvatarUrl(fileUrl);
        // 3.2 组装 accessToken refreshToken
        accountOutputVO.setAccessToken(accessToken);
        accountOutputVO.setRefreshToken(refreshToken);
        return accountOutputVO;
    }

    @Override
    public AccountOutputVO loginAccount(AccountLoginInputVO vo) throws Exception {

        // 1. 通过username查询userMain
        QueryWrapper<UserMain> q = new QueryWrapper<>();
        q.eq("username", vo.getUsername());
        UserMain userMain = userMainDao.selectOne(q);

        if(userMain == null) {
            throw new Exception("用户不存在");
        }

        // 2. 比较密码
        boolean matches = passwordEncoder.matches(vo.getPassword(), userMain.getPassword());

        if(!matches) {
            throw new Exception("密码错误");
        }

        // 2. 1 获取用户权限
        Set<String> authorities = getUserAuthorities(userMain.getId());
        // 3. 生成token
        String accessToken = JwtUtility.generateAccessToken(userMain.getId(), userMain.getUsername(), authorities);

        String refreshToken = JwtUtility.generateRefreshToken(userMain.getId());


        // 4. 组装vo
        AccountOutputVO accountOutputVO = new AccountOutputVO();
        accountOutputVO.setId(userMain.getId());
        accountOutputVO.setUsername(userMain.getUsername());
        accountOutputVO.setNickname(userMain.getNickname());

        String[] split = userMain.getAvatarAddr().split(":");

        String fileUrl = fileService.getFileUrl(split[0], split[1]);

        accountOutputVO.setAvatarUrl(fileUrl);
        accountOutputVO.setAccessToken(accessToken);
        accountOutputVO.setRefreshToken(refreshToken);

        return accountOutputVO;
    }

    @Override
    public AccountOutputVO loginByEmail(UserAccountLoginByEmailInputVO vo) throws Exception {
        // 1. 获取邮箱对应的验证码
        String code = (String)redisTemplate.opsForValue().get("email:" + vo.getEmail());

        // 1.1 如果不存在表示错误或者验证码过期
        if(code == null) {
            throw new Exception("验证码过期或者不存在");
        }

        // 1.2 如果存在 则 对比 输入验证码是否正确 不正确抛出异常
        if(!vo.getCode().equals(code)) {
            throw new Exception("验证码错误");
        }

        // 2 查看UserAccount是否有注册Email的账户
        redisTemplate.delete("email:" + vo.getEmail());
        QueryWrapper<UserAccount> q = new QueryWrapper<>();
        q.eq("type", AccountType.EMAIL);
        q.eq("auth_key", vo.getEmail());
        UserAccount userAccount = userAccountDao.selectOne(q);

        // 2.1  如果有登录这个User
        if(userAccount != null) {
            UserMain userMain = userMainDao.selectById(userAccount.getUserId());

            Set<String> userAuthorities = getUserAuthorities(userMain.getId());

            String accessToken = JwtUtility.generateAccessToken(userMain.getId(), userMain.getUsername(), userAuthorities);

            String refreshToken = JwtUtility.generateRefreshToken(userMain.getId());


            AccountOutputVO accountOutputVO = new AccountOutputVO();
            accountOutputVO.setId(userMain.getId());
            accountOutputVO.setUsername(userMain.getUsername());
            accountOutputVO.setNickname(userMain.getNickname());

            String[] split = userMain.getAvatarAddr().split(":");
            String fileUrl = fileService.getFileUrl(split[0], split[1]);

            accountOutputVO.setAvatarUrl(fileUrl);
            accountOutputVO.setAccessToken(accessToken);
            accountOutputVO.setRefreshToken(refreshToken);

            return accountOutputVO;
        }


        // 2.2 如果没有 注册一个User 随机昵称 随机username 随机密码 随机头像

        // 2.2.1 创建userMain
        UserMain userMain = new UserMain();
        userMain.setNickname("lab-user");
        userMain.setAvatarAddr(BucketTpye.USER_AVATAR + ":" + "Default.jpg");

        String prefix = "lab_";
        UUID uuid = UUID.randomUUID();
        long hash = uuid.getMostSignificantBits() ^ uuid.getLeastSignificantBits();
        String hex = String.format("%020x", hash).substring(0, 20 - prefix.length());
        String randomUsernma =  prefix + hex; // 示例：USER_8c943921d83e

        userMain.setUsername(randomUsernma);
        userMain.setPassword(null);
        userMainDao.insert(userMain);

        // 2.2.2 创建userAccount
        UserAccount account = new UserAccount();
        account.setAccessCode(null);
        account.setUserId(userMain.getId());
        account.setAuthKey(vo.getEmail());
        account.setType(AccountType.EMAIL);

        userAccountDao.insert(account);

        // 2.3 绑定基础用户权限
        Set<String> userAuthorities = bindBaseUserAuth(userMain.getId());

        String accessToken = JwtUtility.generateAccessToken(userMain.getId(), userMain.getUsername(), userAuthorities);

        String refreshToken = JwtUtility.generateRefreshToken(userMain.getId());

        AccountOutputVO accountOutputVO = new AccountOutputVO();
        accountOutputVO.setId(userMain.getId());
        accountOutputVO.setUsername(userMain.getUsername());
        accountOutputVO.setNickname(userMain.getNickname());

        String[] split = userMain.getAvatarAddr().split(":");
        String fileUrl = fileService.getFileUrl(split[0], split[1]);

        accountOutputVO.setAvatarUrl(fileUrl);
        accountOutputVO.setAccessToken(accessToken);
        accountOutputVO.setRefreshToken(refreshToken);

        return accountOutputVO;
    }



    @Override
    public String getEmailCode(UserAccountEmailCodeInputVO vo) {

        Random random = new Random();
        int randomNumber = 1000 + random.nextInt(9000);

        redisTemplate.opsForValue().set("email:"+vo.getEmail(), randomNumber + "", 5, TimeUnit.MINUTES);


        uaaProducer.send(vo.getEmail() + ":" + randomNumber);

        return "";
    }

    @Override
    @Transactional
    public AccountOutputVO callback(String code) throws Exception {

        // 1. gitee APP 参数

        String GITEE_OAUTH_URL = "https://gitee.com/oauth/token";

        String CODE = "deede528705224c73366f25869930cf34911b1508152e3ca6b89184aefe2d541";

        String GITEE_OAUTH_CLIENT_ID = "3c05d9c108d02cc5a3efa67d61656953ec7351c67d29f266c78034b4f95016c0";

        String GITEE_OAUTH_CLIENT_SECRET = "ed283de6420307ed59abf607d36ae65fc61b27dea4f1039a7f8657ff6f3f5768";

        String GITEE_OAUTH_REDIRECT_URI = "https://wujianming.xyz/user/account/callback_oauth2_gitee";

        String GITEE_USER_INFO_URL = "https://gitee.com/api/v5/user";

        // 2. 获取accessToken

        String body = "grant_type=authorization_code&code=" + code +
                "&client_id="+ GITEE_OAUTH_CLIENT_ID +
                "&client_secret="+ GITEE_OAUTH_CLIENT_SECRET +
                "&redirect_uri=" + GITEE_OAUTH_REDIRECT_URI;

        log.info("gitee oauth2 request token body: {}", body);

        HttpRequest requestForToken =  HttpRequest.newBuilder()
                .uri(URI.create(GITEE_OAUTH_URL))
                .header("Content-Type", "application/x-www-form-urlencoded")
                .POST(HttpRequest.BodyPublishers.ofString(body))
                .build();

        ObjectMapper mapper = new ObjectMapper();

        String accessToken = null;

        try {
            HttpResponse<String> response = client.send(
                    requestForToken,
                    HttpResponse.BodyHandlers.ofString() // 响应体转为字符串
            );

            log.info("gitee oauth2 response token body: {}", response.body());

            JsonNode rootNode = mapper.readTree(response.body());


            accessToken = rootNode.get("access_token").asText(); // 获取文本值

            log.info("获取gitee accessToken: " + accessToken);

        } catch (IOException | InterruptedException e) {

            e.printStackTrace();
        }

        // 3. 使用accessToken 获取 userInfo

        if(accessToken == null) {
            throw new Exception("获取 gitee token 失败");
        }

        // GET 请求（JSON 数据） 获取用户信息
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(GITEE_USER_INFO_URL))
                .header("Authorization", "Bearer " + accessToken)
                .GET()
                .build();


        String giteeId = null;
        String giteeName = null;
        String avatarUrl = null;
        try {
            HttpResponse<String> response = client.send(
                    request,
                    HttpResponse.BodyHandlers.ofString() // 响应体转为字符串
            );
            log.info("gitee userinfo JSON: {}", response);

            JsonNode rootNode = mapper.readTree(response.body());
            giteeId = rootNode.get("id").asText(); // 获取文本值
            giteeName = rootNode.get("name").asText(); // 获取文本值
            avatarUrl = rootNode.get("avatar_url").asText();

            log.info("gitee userinfo id: {},name: {},avatarUrl: {}", giteeId, giteeName, avatarUrl);
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }

        // 4. 通过 giteeId 获取用户信息
        QueryWrapper<UserAccount> q = new QueryWrapper<>();
        q.eq("auth_key", giteeId);
        UserAccount userAccount = userAccountDao.selectOne(q);

        // 4.1 用户存在 登录用户
        if(userAccount != null) {
            UserMain userMain = userMainDao.selectById(userAccount.getUserId());

            Set<String> userAuthorities = getUserAuthorities(userMain.getId());

            String accessToken1 = JwtUtility.generateAccessToken(userMain.getId(), userMain.getUsername(), userAuthorities);

            String refreshToken = JwtUtility.generateRefreshToken(userMain.getId());


            AccountOutputVO accountOutputVO = new AccountOutputVO();
            accountOutputVO.setId(userMain.getId());
            accountOutputVO.setUsername(userMain.getUsername());
            accountOutputVO.setNickname(userMain.getNickname());
            accountOutputVO.setAvatarUrl(userMain.getAvatarAddr());
            accountOutputVO.setAccessToken(accessToken1);
            accountOutputVO.setRefreshToken(refreshToken);

            return accountOutputVO;
        }

        // 4.2 用户不存在 新建用户并登录
        //  如果没有 注册一个User 随机昵称 随机username 随机密码 随机头像

        //  创建userMain
        UserMain userMain = new UserMain();
        userMain.setNickname(giteeName);

        assert avatarUrl != null;

        log.info("gitee avatarUrl : {}", avatarUrl);

//        HttpRequest requestForImage = HttpRequest.newBuilder()
//                .uri(URI.create(avatarUrl))
//                .timeout(Duration.ofSeconds(30))
//                .build();
//
//        // 获取图片数据流
//        HttpResponse<InputStream> response = client.send(
//                requestForImage,
//                HttpResponse.BodyHandlers.ofInputStream()
//        );
//        log.info("gitee HttpResponse<InputStream>: {}", response);
//        // 2. 获取图片长度（MinIO 要求）
//        Long contentLength = Long.parseLong(response.headers()
//                        .firstValue("Content-length")
//                        .orElseThrow(() -> new IllegalStateException("无法获取文件长度"))
//        );
//
//
//        String contentType = response.headers()
//                        .firstValue("Content-type")
//                        .orElseThrow(() -> new IllegalStateException("无法获取文件类型"));
//
//
//        URI url = new URI(avatarUrl);
//        // 提取路径并获取文件名
//        Path path = Paths.get(url.getPath());
//        String fileName = path.getFileName().toString();

//        String bucketName = "user-avatar";
//        UploadUrlInputDTO dto = new UploadUrlInputDTO();
//        dto.setBucket(bucketName);
//        dto.setObjectName(fileName);
//        String uploadUrl = fileService.getUploadUrl(dto);
//
//        HttpHeaders headers = new HttpHeaders();
//        headers.setContentType(MediaType.parseMediaType(contentType));
//        headers.setContentLength(contentLength);

//        Resource resource = new InputStreamResource(response.body()) {
//
//            @Override
//            public long contentLength() throws IOException {
//                return response.body().available(); // 必须重写，否则无法计算流大小
//            }
//
//            @Override
//            public String getFilename() {
//                return fileName; // 必须重写文件名，否则服务端可能无法识别
//            }
//        };

//        HttpEntity<Resource> requestEntity = new HttpEntity<>(resource, headers);
//
//        log.info("请求体： {}", requestEntity);
//        log.info("上传地址是：{}", uploadUrl);
//        restTemplate.put(uploadUrl, requestEntity);
//
//        ResponseEntity<String> responseEntity = restTemplate.exchange(
//                uploadUrl,
//                HttpMethod.PUT,
//                requestEntity,
//                String.class
//        );
        String fileN = fileService.uploadFile(BucketTpye.USER_AVATAR, avatarUrl);

//        log.info("返回体：{}", responseEntity);

        userMain.setAvatarAddr(BucketTpye.USER_AVATAR + ":" + fileN);

        String prefix = "lab_";
        UUID uuid = UUID.randomUUID();
        long hash = uuid.getMostSignificantBits() ^ uuid.getLeastSignificantBits();
        String hex = String.format("%020x", hash).substring(0, 20 - prefix.length());
        String randomUsernma =  prefix + hex; // 示例：USER_8c943921d83e

        userMain.setUsername(randomUsernma);
        userMain.setPassword(null);
        userMainDao.insert(userMain);

        // 2.2.2 创建userAccount
        UserAccount account = new UserAccount();
        account.setAccessCode(null);
        account.setUserId(userMain.getId());
        account.setAuthKey(giteeId);
        account.setType(AccountType.GITEE);

        userAccountDao.insert(account);

        // 2.3 绑定基础用户权限
        Set<String> userAuthorities = bindBaseUserAuth(userMain.getId());

        String accessToken1 = JwtUtility.generateAccessToken(userMain.getId(), userMain.getUsername(), userAuthorities);

        String refreshToken = JwtUtility.generateRefreshToken(userMain.getId());

        AccountOutputVO accountOutputVO = new AccountOutputVO();
        accountOutputVO.setId(userMain.getId());
        accountOutputVO.setUsername(userMain.getUsername());
        accountOutputVO.setNickname(userMain.getNickname());
        accountOutputVO.setAvatarUrl(userMain.getAvatarAddr());
        accountOutputVO.setAccessToken(accessToken1);
        accountOutputVO.setRefreshToken(refreshToken);

        return accountOutputVO;
    }

    private Set<String> getUserAuthorities(Long userId) {
        Set<String> authorities = null;

        // 从缓存中获取
        authorities  = (Set<String>)redisTemplate.opsForValue().get("userAuthorities_" + userId.toString());

        // 2.1.1 从用户角色关系表获取权限列表
        if (authorities == null) {
            authorities = new TreeSet<>();

            QueryWrapper<UserAuthUserRoleR> qu = new QueryWrapper<>();
            qu.eq("user_id", userId);
            List<UserAuthUserRoleR> userAuthUserRoleRS = userAuthUserRoleRDao.selectList(qu);

            for (UserAuthUserRoleR r : userAuthUserRoleRS) {
                Long roleId = r.getRoleId();
                UserAuthRole userAuthRole = userAuthRoleDao.selectById(roleId);

                QueryWrapper<UserAuthRoleAuthorityR> que = new QueryWrapper<>();
                que.eq("role_id", userAuthRole.getId());
                List<UserAuthRoleAuthorityR> userAuthRoleAuthorityR = userAuthRoleAuthorityRDao.selectList(que);

                for (UserAuthRoleAuthorityR r1 : userAuthRoleAuthorityR) {
                    UserAuthAuthority userAuthAuthority = userAuthAuthorityDao.selectById(r1.getAuthorityId());
                    authorities.add(userAuthAuthority.getAuthorityName());
                }

            }

            // 2.1.2 从用户权限关系表获取权限列表
            QueryWrapper<UserAuthUserAuthorityR> quer = new QueryWrapper<>();
            quer.eq("user_id", userId);
            List<UserAuthUserAuthorityR> userAuthUserAuthorityRS = userAuthUserAuthorityRDao.selectList(quer);


            for (UserAuthUserAuthorityR r2 : userAuthUserAuthorityRS) {
                UserAuthAuthority userAuthAuthority = userAuthAuthorityDao.selectById(r2.getAuthority_id());
                authorities.add(userAuthAuthority.getAuthorityName());
            }


            redisTemplate.opsForValue().set("userAuthorities_" + userId, authorities);
        }
        return  authorities;
    }

    private Set<String> bindBaseUserAuth(Long userId) {

        UserAuthUserRoleR r = new UserAuthUserRoleR();
        r.setUserId(userId);
        r.setRoleId(Role.BASEUSER_ID);
        userAuthUserRoleRDao.insert(r);

        // 1.4 查询基础用户权限
        List<UserAuthRoleAuthorityR> userAuthRoleAuthorityRS = null;
        // 1.4.1 先从缓存拿
        userAuthRoleAuthorityRS = (List<UserAuthRoleAuthorityR>)redisTemplate.opsForValue().get(Role.BASEUSER_ID.toString());
        // 1.4.2 没有，查数据库，
        if (userAuthRoleAuthorityRS == null) {

            QueryWrapper<UserAuthRoleAuthorityR> q = new QueryWrapper<>();
            q.eq("role_id", Role.BASEUSER_ID);
            userAuthRoleAuthorityRS = userAuthRoleAuthorityRDao.selectList(q);

            log.info(userAuthRoleAuthorityRS.toString());
            redisTemplate.opsForValue().set(Role.BASEUSER_ID.toString(), userAuthRoleAuthorityRS);


        }
        Set<String> userAuthorities = new TreeSet<>();

        for(UserAuthRoleAuthorityR uarar : userAuthRoleAuthorityRS) {
            Long authorityId = uarar.getAuthorityId();
            log.info("authorityId: {}", authorityId);

            UserAuthAuthority userAuthAuthority = null;
            userAuthAuthority = (UserAuthAuthority)redisTemplate.opsForValue().get(authorityId.toString());

            if (userAuthAuthority == null) {
                userAuthAuthority = userAuthAuthorityDao.selectById(authorityId);
                redisTemplate.opsForValue().set(authorityId.toString(), userAuthAuthority);
            }


            userAuthorities.add(userAuthAuthority.getAuthorityName());
        }

        return userAuthorities;
    }
}
